package engine;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Date;
import java.util.LinkedList;

import util.MoveParser;

import beans.AnalysisInformation;
import beans.Color;
import beans.Move;
import board.Board;



/**
 * This class can be used to manage a chess engine 
 * 
 * @author Josep M Sobrepere
 *
 */
public class ChessEngine{	
	private Runtime run = Runtime.getRuntime();
	private Process pr;
	
	private String name;
	
	private String path;	

	
	public String getName() {
		return name;
	}

	

	public String getPath() {
		return path;
	}


	/**	 * 
	 * @param chessEnginePath the path of the chess engine
	 * @throws Exception
	 */
	public ChessEngine(String chessEnginePath) throws Exception  {
		Exception e = null;
		this.path=chessEnginePath;
		
		try {
			pr = run.exec(this.path);
		} catch (IOException e2) {
			pr.destroy();
			e= new Exception("UCI KO");
			throw e;
		}
		
		OutputStream prOS = pr.getOutputStream();
		PrintStream out = new PrintStream(prOS, true);
		BufferedReader br = new BufferedReader(new InputStreamReader( pr.getInputStream()));
		
		out.println("uci");
				
		String UciOutup;
		boolean end=false;		
		int pos= 0;
		Date start = new Date();
		
		int time=0;
		try{
			do{			
					if(br.ready()){
						UciOutup = br.readLine();
						if(UciOutup.startsWith("id name ")){
							this.name= UciOutup.substring(8);
						}				
						end = (UciOutup.indexOf("uciok")!=-1);
					}
				
				time = (int) ((new Date()).getTime()-start.getTime());
				if(time>=500){
					e= new Exception("UCI KO");
					end=true;
				}
			}while(!end);
		}catch (IOException e1) {
			e= new Exception("UCI KO");
		}finally{
			pr.destroy();
		}
		
		if(e!=null)
			throw e;
	}
	
		
	/**
	 * Starts the chess engine
	 * @throws IOException
	 */
	public void start() throws IOException{
		pr = run.exec(this.path);
	}
	/**
	 * stops the chess engine
	 * @throws IOException
	 */
	public void stop() throws IOException{
		pr.destroy();
	}
	
	/**
	 * this function analyzes a move in a certain amount of time
	 * @param move the move to be analyzed
	 * @param time the amount of time that the move is suposed to be analyzed
	 * @return the analysis information obtained by the chess engine
	 * @throws IOException
	 */
	public AnalysisInformation analyzeMove(Move move, int time) throws IOException{		
		StringBuilder sb = new StringBuilder();
		long timeStart = new Date().getTime();
		
		int score=0;
		int scoreUp=0;
		int scoreDown=0;
		int mate=0;
		String bestContinuation = "";
		
		LinkedList<Move> moves = new LinkedList<Move>();
				
		Move tempMove=move;		
		do{
			moves.addFirst(tempMove);
			sb.insert(0, " " + tempMove.toLongAlgebraicFormat());
			tempMove=tempMove.getPreviousMove();
		}while(tempMove!=null);
		
		sb.insert(0, "position startpos moves");
		
		long timeSpent = new Date().getTime() - timeStart;
		System.out.println("time spent steap0: " + timeSpent);
		
		OutputStream prOS = pr.getOutputStream();
		PrintStream out = new PrintStream(prOS, true);
		BufferedReader br = new BufferedReader(new InputStreamReader( pr.getInputStream()));
		
		out.println(sb.toString());
		out.println("go movetime " + time);
		
		String UciOutup;
		boolean end=false;		
		int pos= 0;
		do{
			if(br.ready()){
				UciOutup = br.readLine();
				pos = UciOutup.indexOf(" pv ");	
				if(pos != -1){
					bestContinuation = UciOutup.substring(pos+4);
				}
				pos = UciOutup.indexOf(" score ");
				if(pos != -1){					
					pos = UciOutup.indexOf(" mate ");
					if(pos != -1){
						mate = Integer.valueOf(getWordAfterStr(UciOutup, " mate ")).intValue();						
						int advantage= ((move.getPiece().getColor()==Color.WHITE)?1:-1) * (((bestContinuation.split(" ").length + 1)%2==0)?1:-1);
						score = 29999 * advantage;
					}else{						
						if(UciOutup.indexOf(" cp ") != -1){
							int sc = Integer.valueOf(getWordAfterStr(UciOutup, " cp ")).intValue() * ((move.getPiece().getColor()==Color.WHITE)?-1:1);
							if(UciOutup.indexOf(" lowerbound ")!=-1){
								scoreDown=sc;
							}else if(UciOutup.indexOf(" upperbound ")!=-1){
								scoreUp=sc;
							}else{
								score=sc;								
							}												  	 
						}
					}
				}
				end = (UciOutup.indexOf("bestmove")!=-1);
			}			
		}while(!end);
		
		timeSpent = (new Date().getTime() - timeStart)-timeSpent;
		System.out.println("time spent steap1: " + timeSpent + ", it was suposed to spent " + time);
		
		
		
		Board board = new Board();
		
		int moveNumber = 0;
		for(Move mv: moves){
			board.registerMove(mv);
			moveNumber++;
		}
		
		LinkedList<Move> bestMoveContinuation = new LinkedList<Move>();		
		String movesLongNotation[] = bestContinuation.split(" ");
		
		
		
		for(String moveLong:movesLongNotation){
			Move mv = MoveParser.fromLongNotation(moveLong, board, (moveNumber/2)+1);
			if(bestMoveContinuation.size()>0){
				bestMoveContinuation.getLast().setNextMove(mv);
				mv.setPreviousMove(bestMoveContinuation.getLast());
			}
			bestMoveContinuation.add(mv);
			moveNumber++;
		}
		
		if(scoreDown != scoreUp){
			score = (scoreDown+scoreUp)/2; 
		}else if(scoreDown!=0){
			score= scoreUp;
		}else{
			scoreDown=score;
			scoreUp=score;
		}
		
		timeSpent = (new Date().getTime() - timeStart)-timeSpent;
		System.out.println("time spent steap2: " + timeSpent + " score: " + score);
		System.out.println();
		
		return new AnalysisInformation(move, mate, score, time, false, bestMoveContinuation,scoreUp,scoreDown);
	}
	
	private String getWordAfterStr(String completeString, String after){		
		return completeString.substring(completeString.indexOf(after)+after.length(), completeString.indexOf(" ", completeString.indexOf(after)+after.length()));
	}
	
	
	
	
	

}
